/************************************************************************************
 * 
 *  MIT License
 *
 *  Copyright (c) 2022 shaoguangcn<shaoguangcn@163.com>
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *
 ************************************************************************************/

#ifndef APPLICATION_HPP
#define APPLICATION_HPP

#include <string>
#include <vector>
#include <atomic>

/**
 * The Application class is used to provide the loop for main thread and some useful functions.
 * 
 * <code>
 *   int main(int argc, char* argv[])
 *   {
 *        Application::Instance()->Initialize(argc, argv);
 *        // do something and may call Application::Instance()->Exit() in a thread.
 *        return Application::Instance()->Exec();
 *   }
 * </code>
 */
class Application
{
public:
    static Application* Instance();

    /**
     * Returns the process ID of the calling process.
     */
    static std::int64_t GetApplicationPid();

    /**
     * initialize the application with arguments of the main() function.
     */
    void Initialize(int& argc, char* argv[]);

    /**
     * Returns the reference of application arguments count.
     */
    int& GetArgumentsCountRef();

    /**
     * Returns the count of application arguments.
     */
    int GetArgumentsCount() const;

    /**
     * Returns the original arguments which set by Initialize().
     * 
     * \sa GetArguments(), GetArgumentsCount()
     */
    char** GetOriginalArguments();

    /**
     * Returns the original arguments which set by Initialize().
     * 
     * \sa GetArguments(), GetArgumentsCount()
     */
    const char * const * GetOriginalArguments() const;

    /**
     * Returns the list of the application arguments.
     * 
     * Usually the first element of arguments is the application name, the second element of arguments is the first argument.
     * 
     * \sa GetOriginalArguments()
     */
    std::vector<std::string> GetArguments() const;

    static std::string ApplicatoinFilePath();

    /**
     * Returns the file path of the application executable file.
     * 
     * For example, if application executable file is "/usr/local/bin/app", this function will return "/usr/local/bin/app".
     * 
     * If call system API failed and the Application is not initialized, returns empty string.
     * 
     * \sa GetApplicationDirPath(), GetApplicationName()
     */
    std::string GetApplicationFilePath() const;

    static std::string ApplicationDirPath();

    /**
     * Returns the directory that contains the application executable file.
     * 
     * For example, if application executable file is "/usr/local/bin/app", this function will return "/usr/local/bin".
     * 
     * If call system API failed and the Application is not initialized, returns empty string.
     * 
     * \sa GetApplicationFilePath(), GetApplicationName()
     */
    std::string GetApplicationDirPath() const;

    static std::string ApplicationName();
    
    /**
     * Returns the application name.
     * 
     * For example:
     * on Linux, if application executable file is "/usr/local/bin/app", this function will return "app";
     * on Windows, if application executable file is "C:\\app.exe", this function will return "app.exe".
     * 
     * If the Application is not initialized and call system API failed, returns empty string.
     * 
     * \sa GetApplicationFilePath(), GetApplicationDirPath()
     */
    std::string GetApplicationName() const;

    /**
     * Enters the main loop and waits until Exit() is called.
     * 
     * Returns the value that was passed to Exit() (which is 0 if Exit() is called via Quit()).
     * 
     * \sa Exit(), Quit()
     */
    int Exec();

    /**
     * Tells the application to exit with a return code.
     * 
     * After this function has been called, the application leaves the main event loop and returns from
     * from the call to to Exec(). The Exec() function returns \a returnCode. 
     * If the event loop is not running, this function does nothing.
     * 
     * \sa Quit()
     */
    void Exit(int exitCode = 0);

    /**
     * Tells the application to exit with return code 0 (success). Equivalent to calling Exit(0).
     */
    void Quit();

    /**
     * Call std::exit() to end this process.
     */
    void ForceExit(int exitCode = 0);

    /**
     * Call std::quick_exit() to end this process.
     */
    void ForceQuickExit(int exitCode = 0);

protected:
    Application();
    ~Application();
    Application(const Application&) = delete;
    Application& operator=(const Application&) = delete;

protected:
    int* argc_{ nullptr };
    char** argv_{ nullptr };
    std::atomic_bool exit_flag_{ false };
    std::atomic_int exit_code_{ 0 };
};

#endif // APPLICATION_HPP